package sk.datalan.sparqlreasoner;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import sk.datalan.core.Utils;

/*
 * Trieda pomocou ktorej odpalujeme multithreadovo vela query vzdy na jednom procesore
 */
public class SparqlResoning {

	private List<Rule> rules;
	private ExecutorService executor;

	public SparqlResoning( File ruleFile ) {
		this.executor = Executors.newFixedThreadPool( 2 ); //pocet jadier na PC.
		this.rules = parseRuleFile( ruleFile ); //sparsuje subor, ktory obsahuje jednotlive query
	}

	private List<Rule> parseRuleFile( File ruleFile ) {
		try {
			List<Rule> rules = new ArrayList<Rule>();
			String line = null;
			String tempLine = null;

			BufferedReader input = new BufferedReader( new FileReader( ruleFile ) );
			String RuleId = null;
			String query = null;
			List<String> dependsOn = new ArrayList<String>();

			while ( ( line = input.readLine() ) != null ) {
				line = line.trim();
				if ( line.length() == 0 || line.startsWith( "#" ) ) {
					continue;
				}

				if ( line.startsWith( "RuleId:" ) ) {
					RuleId = line.substring( 7 ).trim();
					while ( ( tempLine = input.readLine() ) != null ) {
						tempLine = tempLine.trim();
						if ( tempLine.length() == 0 || tempLine.startsWith( "#" ) ) {
							continue;
						}
						if ( tempLine.startsWith( "DependsOn:" ) ) {
							for ( String string : tempLine.substring( 10 ).split( ";" ) ) {
								if ( string.trim().length() > 0 ) {
									dependsOn.add( string.trim() );
								}
							}
						} else if ( tempLine.startsWith( "Query:" ) ) {
							query = tempLine.substring( 6 ).trim();
							break;
						} else {
							Utils.log( "Error during parsing! Rule:  '" + RuleId + "' is corrupted!" );
							break;
						}
					}
					if ( query == null ) {
						Utils.log( "Error during parsing! Rule:  '" + RuleId + "' is corrupted - no query available" );
					}
					rules.add( new Rule( RuleId, query, dependsOn ) );
					RuleId = null;
					dependsOn = new ArrayList<String>();
					query = null;
				}
			}

			input.close();
			return rules;
		} catch ( Exception e ) {
			Utils.log( "Unable to load query file '" + ruleFile.getAbsolutePath() + "':" + e );
			return null;
		}
	}

	public void showRules() {
		String dep = "";
		for ( Rule rule : this.rules ) {
			System.out.println( "RuleId:" + rule.getId() + ";" );
			for ( String d : rule.getDependsOn() ) {
				dep += d + ";";
			}

			System.out.println( "DependsOn:" + dep + ";" );
			System.out.println( "Query:" + rule.getQuery() + ";" );
			System.out.println( "\n\n" );
			dep = "";
		}
	}

	public void start() {
		boolean loop = true;
		long startTime = System.currentTimeMillis();
		Utils.log( "SPARQL reasoning has started!\n" );
		while ( loop ) {
			loop = false;
			for ( Rule r : this.rules ) {
				if ( !r.isExecuting() && !r.isComplete() ) {
					loop = true;
					if ( r.isReady( this.rules ) ) {
						r.setIsExecuting( true );
						this.executor.execute( r );
					}
				}
			}
			try {
				Thread.sleep( 1000 );
			} catch ( InterruptedException e ) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.executor.shutdown();
		try {
			this.executor.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
			Utils.log( "Reasoning finished in " + ( System.currentTimeMillis() - startTime ) + "ms" );

		} catch ( InterruptedException e ) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void reset() {

	}

	public void stop() {
		this.executor.shutdownNow();
	}

}
